using System;

namespace Dida
{
	[Dxml.DidaObject(TagName="Oscillator")]
	public class Oscillator : IAniValue<float>
	{
		float mid;
		float amplitude;

		float t;
		float value;

		[Dxml.AutoXmlConstructor]
		public Oscillator(float min, float max, float phaseLen)
		{
			t = 0;

			mid = (min + max) * .5f;
			amplitude = max - mid;
		}

		public float CurrentValue
		{
			get { return value; }
		}

		public void Start()
		{
			value = (float)(Math.Sin(t) * amplitude + mid);
		}

		public void Update(float timeDelta)
		{
			t += timeDelta;
			t = (float)Math.IEEERemainder(t, 2 * Math.PI);
			value = (float)(Math.Sin(t) * amplitude + mid);
		}
	}

	[Dxml.DidaObject]
	public class TriOscillator : IAniValue<float>
	{
		[Dxml.Parsable]
		public enum Direction
		{
			Up,
			Down,
		}

		float min;
		float max;
		float doubleFreq;

		float cur;
		Direction dir;

		[Dxml.AutoXmlConstructor]
		public TriOscillator(float min, float max, float phaseLen,
			float initialValue,	Direction initialDir )
		{
			this.min = min;
			this.max = max;
			doubleFreq = 2 / phaseLen;
			cur = initialValue;
			dir = initialDir;
		}

		public void Start()
		{
			// nothing to do...
		}

		public void Update(float timeDelta)
		{
			if (dir == Direction.Up)
				cur += doubleFreq * timeDelta;
			else
				cur -= doubleFreq * timeDelta;

			while (cur < min || cur > max)
			{
				if (cur > max)
				{
					cur = max - (cur - max);
					dir = Direction.Down;
				}
				
				if (cur < min)
				{
					cur = min - (cur - min);
					dir = Direction.Up;
				}
			}
		}

		public float CurrentValue
		{
			get { return cur; }
		}
	}

	[Dxml.DidaObject]
	public class Orbiter : IAniValue<Vector3D>
	{
		Vector3D center;
		Vector3D tangent1;
		Vector3D tangent2;
		float speed;

		float t;
		Vector3D cur;

		[Dxml.AutoXmlConstructor]
		public Orbiter(Vector3D center, float radius, Quaternion rotation, float orbitTime)
		{
			this.center = center;
			this.speed = (float)Math.PI * 2 / orbitTime;

			tangent1 = rotation.GetRotatedVector(new Vector3D(1, 0, 0)) * radius;
			tangent2 = rotation.GetRotatedVector(new Vector3D(0, 1, 0)) * radius;

			t = 0;
		}

		public Vector3D CurrentValue
		{
			get { return cur; }
		}

		public void Start()
		{
			Update(0);
		}

		public void Update(float timeDelta)
		{
			t += timeDelta * speed;
			if (t > 2 * Math.PI)
			{
				Rotation.Normalize(ref t);
			}

			float s = (float)Math.Sin(t);
			float c = (float)Math.Cos(t);

			cur = tangent1 * c + tangent2 * s + center;
		}
	}
}
